package com.firesoon;

import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;

/**
 * @author yakun.zeng
 * @version 1.0
 * @description: TODO
 * @date 2025/7/28 20:19
 */
@Slf4j
public class ControllerInterceptor {

    /**
     *
     * @param method @Origin可以绑定到以下类型的参数：
     *               Method 被调用的原始方法
     *               Constructor 被调用的原始构造器
     *               Class 当前动态创建的类
     *               MethodHandle MethodType String 动态类的toString()的返回值 int 动态方法的修饰符
     *
     * @param args @AllArguments绑定所有参数的数组
     *
     * @param callable @SuperCall用于调用父类版本的方法
     * @return @RuntimeType可以用在返回值、参数上，提示ByteBuddy禁用严格的类型检查
     * @throws Exception
     */
    @RuntimeType
    public static Object intercept(@Origin Method method,
                                   @AllArguments Object[] args,
                                   @SuperCall Callable<?> callable) throws Exception {

        Object resObj = null;
        HttpServletRequest request = null;
        long start = System.currentTimeMillis();

        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (requestAttributes != null) {
            request = ((ServletRequestAttributes) requestAttributes).getRequest();
        }

        try {

            resObj = callable.call();
            return resObj;

        } finally {

//            StringBuilder logStr = new StringBuilder("\n");
//
//            if (request != null) {
//                StringBuilder requestHeader = new StringBuilder();
//                Enumeration<String> headerNames = request.getHeaderNames();
//                while (headerNames.hasMoreElements()) {
//                    String name = headerNames.nextElement();
//                    String value = request.getHeader(name);
//                    requestHeader.append("[").append(name).append("]: ").append(value).append("; ");
//                }
//                logStr.append("请求URI: ").append(request.getRequestURI()).append("\n");
//                logStr.append("请求Header: ").append(requestHeader).append("\n");
//            }
//
//            logStr.append("方法名称: ").append(method.getName()).append("\n");
//            StringBuilder paramName = new StringBuilder();
//            StringBuilder paramValue = new StringBuilder();
//            for (int i = 0; i < args.length; i++) {
//                paramName.append("Param[").append(i).append("]: ").append(method.getParameterTypes()[i].getName()).append("; ");
//                paramValue.append("Param[").append(i).append("]: ").append(args[i]).append("; ");
//            }
//            logStr.append("入参名称: ").append(paramName).append("\n");
//            logStr.append("入参内容: ").append(paramValue).append("\n");
//            logStr.append("出参类型: ").append(method.getReturnType().getName()).append("\n");
//            logStr.append("出参结果: ").append(resObj).append("\n");
//            logStr.append("方法耗时: ").append(System.currentTimeMillis() - start).append(" ms");
//
//            log.info(logStr.toString());

            log.info("method: " + request.getMethod());
            log.info("url: " + request.getRequestURL().toString());
            log.info("uri: " + request.getRequestURI());
            log.info("query: " + request.getQueryString());

            StringBuilder requestHeader = new StringBuilder();
            Enumeration<String> headerNames = request.getHeaderNames();
            while (headerNames.hasMoreElements()) {
                String name = headerNames.nextElement();
                String value = request.getHeader(name);
                requestHeader.append("[").append(name).append("]: ").append(value).append("; ");
            }
            log.info("header: " + requestHeader.toString());
            String body = request.getReader().lines().collect(Collectors.joining());
            log.info("body: " + body);

            String param = "";
            for (Map.Entry<String, String[]> entry : request.getParameterMap().entrySet()) {
                param += entry.getKey() + "=" + entry.getValue() + "&";
            }
            log.info("param: " + param);
        }
    }
}
